using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Engine;
using GameEntitySystem;
using LiteNetLib;
using LiteNetLib.Utils;
using TemplatesDatabase;

namespace Game
{
	public class SubsystemRemoteTransmitter : Subsystem, IUpdateable
	{
		private class RemoteDataPacketWrapper
		{
			[CompilerGenerated]
			private sealed class _003C_003Ec__DisplayClass3_0<T> where T : class, new()
			{
				public RemoteDataSender<T> sender;

				internal void _003CLoadActions_003Eb__0(float dt)
				{
					sender.PollEvents(dt);
				}
			}

			[CompilerGenerated]
			private sealed class _003C_003Ec__DisplayClass3_1<T> where T : class, new()
			{
				public RemoteDataReciver<T> reciver;

				internal void _003CLoadActions_003Eb__1()
				{
					NetworkManager.SubscribePacket(new Action<T, NetPeer>(reciver.OnRecive));
				}

				internal void _003CLoadActions_003Eb__3(float dt)
				{
					reciver.PollEvents(dt);
				}
			}

			[Serializable]
			[CompilerGenerated]
			private sealed class _003C_003Ec__3<T> where T : class, new()
			{
				public static readonly _003C_003Ec__3<T> _003C_003E9 = new _003C_003Ec__3<T>();

				public static Action _003C_003E9__3_2;

				internal void _003CLoadActions_003Eb__3_2()
				{
					NetworkManager.RemoveSubscribtion<T>();
				}
			}

			public Action Subscribe;

			public Action Unsubscribe;

			public Action<float> Update;

			public void LoadActions<T>(IRemoteDataInfo<T> info, bool isSender) where T : class, new()
			{
				if (isSender)
				{
					_003C_003Ec__DisplayClass3_0<T> _003C_003Ec__DisplayClass3_ = new _003C_003Ec__DisplayClass3_0<T>();
					_003C_003Ec__DisplayClass3_.sender = new RemoteDataSender<T>(info);
					Update = new Action<float>(_003C_003Ec__DisplayClass3_._003CLoadActions_003Eb__0);
				}
				else
				{
					_003C_003Ec__DisplayClass3_1<T> _003C_003Ec__DisplayClass3_2 = new _003C_003Ec__DisplayClass3_1<T>();
					_003C_003Ec__DisplayClass3_2.reciver = new RemoteDataReciver<T>(info);
					Subscribe = new Action(_003C_003Ec__DisplayClass3_2._003CLoadActions_003Eb__1);
					Unsubscribe = _003C_003Ec__3<T>._003C_003E9__3_2 ?? (_003C_003Ec__3<T>._003C_003E9__3_2 = new Action(_003C_003Ec__3<T>._003C_003E9._003CLoadActions_003Eb__3_2));
					Update = new Action<float>(_003C_003Ec__DisplayClass3_2._003CLoadActions_003Eb__3);
				}
			}
		}

		private class RemoteDataSender<T> where T : class, new()
		{
			private IRemoteDataInfo<T> info;

			public RemoteDataSender(IRemoteDataInfo<T> info)
			{
				this.info = info;
			}

			public void PollEvents(float dt)
			{
				if (!Time.PeriodicEvent(info.UpdatePeriod, dt))
				{
					return;
				}
				foreach (T item in info.ProducePackets())
				{
					NetworkManager.SendPacketToAll(item, DeliveryMethod.ReliableUnordered, NetworkManager.PeerStage.Ready);
				}
			}
		}

		private class RemoteDataReciver<T> where T : class, new()
		{
			private Queue<T> pendingPackets = new Queue<T>();

			private IRemoteDataInfo<T> info;

			public RemoteDataReciver(IRemoteDataInfo<T> info)
			{
				this.info = info;
			}

			public void OnRecive(T packet, NetPeer peer)
			{
				pendingPackets.Enqueue(packet);
			}

			public void PollEvents(float dt)
			{
				if (Time.PeriodicEvent(info.UpdatePeriod, dt))
				{
					while (pendingPackets.Count > 0)
					{
						info.ApplyPacket(pendingPackets.Dequeue());
					}
				}
			}
		}

		public interface IRemoteDataInfo<T> where T : class, new()
		{
			float UpdatePeriod { get; }

			DeliveryMethod DeliveryMethod { get; }

			IEnumerable<T> ProducePackets();

			void ApplyPacket(T packet);
		}

		private abstract class EntityDataInfo<T, P> : IRemoteDataInfo<EntityDataInfo<T, P>.DataPacket>, IUpdateable where T : class where P : struct
		{
			public class DataPacket
			{
				[CompilerGenerated]
				private int _003CIndex_003Ek__BackingField;

				[CompilerGenerated]
				private P _003CPacket_003Ek__BackingField;

				public int Index
				{
					[CompilerGenerated]
					get
					{
						return _003CIndex_003Ek__BackingField;
					}
					[CompilerGenerated]
					set
					{
						_003CIndex_003Ek__BackingField = value;
					}
				}

				public P Packet
				{
					[CompilerGenerated]
					get
					{
						return _003CPacket_003Ek__BackingField;
					}
					[CompilerGenerated]
					set
					{
						_003CPacket_003Ek__BackingField = value;
					}
				}
			}

			[Serializable]
			[CompilerGenerated]
			private sealed class _003C_003Ec
			{
				public static readonly _003C_003Ec _003C_003E9 = new _003C_003Ec();

				public static Action<NetDataWriter, P> _003C_003E9__9_0;

				public static Func<NetDataReader, P> _003C_003E9__9_1;

				internal void _003CLoad_003Eb__9_0(NetDataWriter w, P p)
				{
					NetworkSerializer.Write(w, p);
				}

				internal P _003CLoad_003Eb__9_1(NetDataReader r)
				{
					return NetworkSerializer.Read<P>(r);
				}
			}

			[CompilerGenerated]
			private sealed class _003CProducePackets_003Ed__12 : IEnumerable<DataPacket>, IEnumerable, IEnumerator<DataPacket>, IDisposable, IEnumerator
			{
				private int _003C_003E1__state;

				private DataPacket _003C_003E2__current;

				private int _003C_003El__initialThreadId;

				public EntityDataInfo<T, P> _003C_003E4__this;

				private Dictionary<int, T>.Enumerator _003C_003E7__wrap1;

				DataPacket IEnumerator<DataPacket>.Current
				{
					[DebuggerHidden]
					get
					{
						return _003C_003E2__current;
					}
				}

				object IEnumerator.Current
				{
					[DebuggerHidden]
					get
					{
						return _003C_003E2__current;
					}
				}

				[DebuggerHidden]
				public _003CProducePackets_003Ed__12(int _003C_003E1__state)
				{
					this._003C_003E1__state = _003C_003E1__state;
					_003C_003El__initialThreadId = Environment.CurrentManagedThreadId;
				}

				[DebuggerHidden]
				void IDisposable.Dispose()
				{
					int num = _003C_003E1__state;
					if (num == -3 || num == 1)
					{
						try
						{
						}
						finally
						{
							_003C_003Em__Finally1();
						}
					}
				}

				private bool MoveNext()
				{
					try
					{
						int num = _003C_003E1__state;
						EntityDataInfo<T, P> entityDataInfo = _003C_003E4__this;
						switch (num)
						{
						default:
							return false;
						case 0:
							_003C_003E1__state = -1;
							_003C_003E7__wrap1 = entityDataInfo.entityDatas.GetEnumerator();
							_003C_003E1__state = -3;
							break;
						case 1:
							_003C_003E1__state = -3;
							break;
						}
						if (_003C_003E7__wrap1.MoveNext())
						{
							KeyValuePair<int, T> current = _003C_003E7__wrap1.Current;
							DataPacket dataPacket = new DataPacket();
							dataPacket.Index = current.Key;
							dataPacket.Packet = entityDataInfo.ProducePacket(current.Value);
							_003C_003E2__current = dataPacket;
							_003C_003E1__state = 1;
							return true;
						}
						_003C_003Em__Finally1();
						_003C_003E7__wrap1 = default(Dictionary<int, T>.Enumerator);
						return false;
					}
					catch
					{
						//try-fault
						((IDisposable)this).Dispose();
						throw;
					}
				}

				bool IEnumerator.MoveNext()
				{
					//ILSpy generated this explicit interface implementation from .override directive in MoveNext
					return this.MoveNext();
				}

				private void _003C_003Em__Finally1()
				{
					_003C_003E1__state = -1;
					((IDisposable)_003C_003E7__wrap1/*cast due to .constrained prefix*/).Dispose();
				}

				[DebuggerHidden]
				void IEnumerator.Reset()
				{
					throw new NotSupportedException();
				}

				[DebuggerHidden]
				IEnumerator<DataPacket> IEnumerable<DataPacket>.GetEnumerator()
				{
					_003CProducePackets_003Ed__12 _003CProducePackets_003Ed__;
					if (_003C_003E1__state == -2 && _003C_003El__initialThreadId == Environment.CurrentManagedThreadId)
					{
						_003C_003E1__state = 0;
						_003CProducePackets_003Ed__ = this;
					}
					else
					{
						_003CProducePackets_003Ed__ = new _003CProducePackets_003Ed__12(0);
						_003CProducePackets_003Ed__._003C_003E4__this = _003C_003E4__this;
					}
					return _003CProducePackets_003Ed__;
				}

				[DebuggerHidden]
				IEnumerator IEnumerable.GetEnumerator()
				{
					return ((IEnumerable<DataPacket>)this).GetEnumerator();
				}
			}

			private Dictionary<int, T> entityDatas = new Dictionary<int, T>();

			private Dictionary<int, P> latestPackets = new Dictionary<int, P>();

			public float UpdatePeriod
			{
				get
				{
					return 0.02f;
				}
			}

			public DeliveryMethod DeliveryMethod
			{
				get
				{
					return DeliveryMethod.Sequenced;
				}
			}

			public int UpdateOrder
			{
				get
				{
					return -1;
				}
			}

			public virtual void Load(SubsystemRemoteEntities entities)
			{
				NetworkManager.RegisterPacket(_003C_003Ec._003C_003E9__9_0 ?? (_003C_003Ec._003C_003E9__9_0 = new Action<NetDataWriter, P>(_003C_003Ec._003C_003E9._003CLoad_003Eb__9_0)), _003C_003Ec._003C_003E9__9_1 ?? (_003C_003Ec._003C_003E9__9_1 = new Func<NetDataReader, P>(_003C_003Ec._003C_003E9._003CLoad_003Eb__9_1)));
				entities.EntityRecordAdded += new Action<int, Entity>(EntityRecordAdded);
				entities.EntityRecordRemoved += new Action<int>(EntityRecordRemoved);
			}

			private void EntityRecordRemoved(int obj)
			{
				entityDatas.Remove(obj);
			}

			private void EntityRecordAdded(int arg1, Entity arg2)
			{
				if (arg2.FindComponent<ComponentPlayer>() == null)
				{
					T val = arg2.FindComponent<T>();
					if (val != null)
					{
						entityDatas.Add(arg1, val);
					}
				}
			}

			[IteratorStateMachine(typeof(EntityDataInfo<, >._003CProducePackets_003Ed__12))]
			public IEnumerable<DataPacket> ProducePackets()
			{
				_003CProducePackets_003Ed__12 _003CProducePackets_003Ed__ = new _003CProducePackets_003Ed__12(-2);
				_003CProducePackets_003Ed__._003C_003E4__this = this;
				return _003CProducePackets_003Ed__;
			}

			public void ApplyPacket(DataPacket packet)
			{
				if (entityDatas.ContainsKey(packet.Index))
				{
					ApplyPacket(entityDatas[packet.Index], packet.Packet);
					latestPackets[packet.Index] = packet.Packet;
				}
			}

			protected abstract P ProducePacket(T component);

			protected abstract void ApplyPacket(T component, P packet);

			public void Update(float dt)
			{
				foreach (int key in entityDatas.Keys)
				{
					ApplyPacket(entityDatas[key], latestPackets[key]);
				}
			}
		}

		private class LocomotionInfo : EntityDataInfo<ComponentLocomotion, LocomotionInfo.LocPacket>
		{
			public struct LocPacket
			{
				public Vector2 LookAngles;
			}

			protected override void ApplyPacket(ComponentLocomotion component, LocPacket packet)
			{
				ReflectionUtils.SetField((object)component, "m_lookAngles", (object)packet.LookAngles);
			}

			protected override LocPacket ProducePacket(ComponentLocomotion component)
			{
				LocPacket result = default(LocPacket);
				result.LookAngles = component.LookOrder;
				return result;
			}
		}

		private class BodyInfo : EntityDataInfo<ComponentBody, BodyInfo.BodyData>
		{
			public struct BodyData
			{
				public Vector3 Position;

				public Quaternion Rotation;

				public Vector3 Velocity;
			}

			protected override void ApplyPacket(ComponentBody body, BodyData data)
			{
				body.Position = data.Position;
				body.Rotation = data.Rotation;
				body.Velocity = data.Velocity;
			}

			protected override BodyData ProducePacket(ComponentBody body)
			{
				BodyData result = default(BodyData);
				result.Position = body.Position;
				result.Rotation = body.Rotation;
				result.Velocity = body.Velocity;
				return result;
			}
		}

		private const float EntityUpdatePeriod = 0.02f;

		private List<RemoteDataPacketWrapper> wrappers = new List<RemoteDataPacketWrapper>();

		public int UpdateOrder
		{
			get
			{
				return -5;
			}
		}

		public void Update(float dt)
		{
			try
			{
				foreach (RemoteDataPacketWrapper wrapper in wrappers)
				{
					wrapper.Update(dt);
				}
			}
			catch (Exception ex)
			{
				Log.Error("Error on packet:\n" + ((ex != null) ? ex.ToString() : null));
			}
		}

		protected override void Load(ValuesDictionary valuesDictionary)
		{
			SubsystemRemoteEntities entities = base.Project.FindSubsystem<SubsystemRemoteEntities>();
			bool value = valuesDictionary.GetValue<bool>("IsClient");
			AddPacketWrapper(new BodyInfo(), entities, !value);
			AddPacketWrapper(new LocomotionInfo(), entities, !value);
			foreach (RemoteDataPacketWrapper wrapper in wrappers)
			{
				Action subscribe = wrapper.Subscribe;
				if (subscribe != null)
				{
					subscribe();
				}
			}
		}

		public override void Dispose()
		{
			foreach (RemoteDataPacketWrapper wrapper in wrappers)
			{
				Action unsubscribe = wrapper.Unsubscribe;
				if (unsubscribe != null)
				{
					unsubscribe();
				}
			}
		}

		private void AddPacketWrapper<T, P>(EntityDataInfo<T, P> info, SubsystemRemoteEntities entities, bool isSender) where T : Component where P : struct
		{
			base.Project.FindSubsystem<SubsystemUpdate>().AddUpdateable(info);
			info.Load(entities);
			RemoteDataPacketWrapper remoteDataPacketWrapper = new RemoteDataPacketWrapper();
			remoteDataPacketWrapper.LoadActions(info, isSender);
			wrappers.Add(remoteDataPacketWrapper);
		}
	}
}
